/** LongInstance2B2 did not work because it was not easy to trick it to use Neighborhood22 instead of Neighborhood2. Therefore, 
  * I creat the LongInstance2B2B class from scratchthat natively uses Neighborhood22 and is not inherited from anything.
  */

#pragma once

#include "neighborhood2.h"
//#include "neighborhood2.h"
#include "imagematrix.h"
#include "coordinate.h"
//#include "eightlayerim.h"
#include <string>
#include <vector>

using namespace std;
class Neighborhood2;
class ImageMatrix;
class LongInstance2;

class LongInstance2B
{
protected:
	vector<Coordinate> coordinate;		//!< When used on an image, this array holds the locations in which this instance exists.
	vector<Neighborhood2> neighborhood2;	//!< The array of neighborhood2. 0 is for current time, 1 for t-1, etc.
	int label;							//!< The label for the instance. Is usually either 0 or 1 (black or white)
	int conflictNum;					//!< Tells the number of instances that conflict this instance.
	
	/** Stores the total hash value for the whole Long Instance, not individual neighborhoods. Unique for up to 8 phases.*/
	unsigned long totalHash;
	
	
public:

	/** Adds a new coordinate as a new place in the image, which this coordinate exists.*/
	void addCoordinate(int row, int col) {this->coordinate.push_back(Coordinate(row,col));}
	void addCoordinate(const Coordinate& c) {this->coordinate.push_back(c);}
	void incrementConflictNum(void) {this->conflictNum++;}
	int getFeatureSize() const; //!< Returns the total number of small elements (pixel states) in the whole IF part.

	int neighborhoodNum(void) const {return (int)this->neighborhood2.size();}	//!< Returns the number of Neighborhood2s in the IF part

	inline Coordinate getCoordinate (int index) const {return this->coordinate[index];}
	void setConflictNum (int val) {this->conflictNum = val;}
	int getConflictNum (void) const {return this->conflictNum;}
	int getRowOfCoordinate (int index) const {return this->coordinate[index].row;}
	int getColOfCoordinate (int index) const {return this->coordinate[index].col;}
	int getLabel (void) const {return this->label;}
	void setLabel (int val);
	void setCoordinate (int index, int row, int col) 
	{
		this->coordinate[index].row = row;
		this->coordinate[index].col = col;
	}

	/** Clears all the members of the object, like it was just constructed.*/
	void clear (void);

	/** Advances the instance in time (e.g. when the instance is for transition from phase 0 to phase 1,
	 * and now we are making it a transition from to phase 0 to phase 2). Adds a new neighborhood2 to the 
	 * existing instance (the neighborhood2 at the most recent phase we have passed) and also updates the
	 * label to the new label.
	 */
	void addPhase (const Neighborhood2& newNB, int newLabel);

	/** Advances the LongInstance2B in time by adding a new neighborhood2 from the given ImageMatrix. The 
	 *  new neighborhood2 will be build around the row and col.
	 */
	void addPhase (const ImageMatrix& image, int row, int col, int nbRadius, int newLabel);
	
	/** Returns the value of the given feature in the given neighborhood2. */
	int getFeature (int neighborhoodIndex, int featureIndex) const;

	/** Returns the value of the given feature, if all the features in all the neighbors are put linearly in line. */
	int getFeature (int longFeatureIndex) const;
		
	unsigned long getTotalHash() const;

	void setFeature (int neighborhoodIndex, int featureIndex, int value);

	void setFeature (int longFeatureIndex, int value);

	/**
	 * returns the number of coordinates in the LongInstance2B.
	 */
	int getOccuranceNum (void) const;
	int getCooNum (void) const {return this->getOccuranceNum();} //!< The same as above, for a more convenient function name

	LongInstance2B & operator= (const LongInstance2B&);	//!< Assignment operator
		
	bool hasSameFeature (const LongInstance2B&) const;		//!< Returns true if the feature array of both objects have the same elements.

	bool hasSameFeature (const vector<Neighborhood2>& nb) const;	//!< Returns true if this longinstance has the same features as the given neighborhood vector.
	
	/** Returns true if both objects have the same partialHash for their corresponding neighborhoods.
	 * i.e. if the neighborhoods are the same.
	 */
	//bool hasSamePartialHash (LongInstance2B& other) const;
	
	void print(void) const;							//!< Prints the feature array to the screen
	
	void printDetail(void) const;						//!< Prints all the member data to the screen

	/** Returns true if this object can be the next phase (i.e. next step) of the given instance.*/
	bool isNextPhase (LongInstance2B& prevPhase) const;

	/** Returns true if this object can be the previous phase (i.e. previous step) of the given instance.*/	 
	bool isPrevPhase (LongInstance2B& nextPhase) const;
	
	/** Updates the totalHash value. */
	void updateTotalHash (void);

	/** Creates the object from a provided totalHash. The base is CELLSTATES 
	 * and the length of the array is size*NEIGHBORHOODSIZE1
	 */
	//void updateFromHash (unsigned long totalHash, int size, int label);

	/** updates the neighborhoods based on the input partialhash array.*/
	//void updateFromPartialHashArray(unsigned long* hashValue, int hashValueSize, int label);

	/** adds the coordinates of the other long instance object to this object. other object is not changed.*/
	void addOthersCoordinates ( const LongInstance2B& other);

	int getNBRadius () const;	//!< Returns the radius size of its Neighborhood2 objects, -1 if neighborhood2 vector is empty.
	//int getTopCooRow (void); //!< Returns the row of the highest coordination in the
	//int getBottomCooRow (void);
	//int getLeftCooCol (void);
	//int getRightCoocol (void);

	/**
	 * Returns the borders of the smallest rectangle that covers all the coordinates in the object.
	 * Parameters are all output parameters.
	 */
	void getCooBorders (int& top, int& bottom, int& left, int& right) const;

	/**
	 * Returnes the net information size (in bits) stored in an object.
	 * The returned value is the minimum number of bits that efficiently can give us all the information
	 * in an object.
	 */
	int getNetInfoSizeInBits(void) const;

	void clearCoordinates(void) {this->coordinate.clear();}

	/** returns the size of one neighborhood2 in the objects, i.e. the number of features in any of the neighborhoods.*/
	int getNeighborhood2Size(void) const;

	LongInstance2B ();

	LongInstance2B (const LongInstance2B& other);

	/** Creates and returns a LongInstance2B from the current object by removing the neighborhood whose index is given.
	 *  \param featureIndex the index of the feature to be removed from ALL the neighborhoods.
	 */
	//LongInstance2B& removeFeature_emptyCoo(int featureIndex) const;
	
	/** Creates the object from a provided string and a label
	 \param feature a string of 0s and 1s, not separated by anything. This function
	 select the chunk of the string for each Neighborhood2 based on the size of NRADIUS.*/
	LongInstance2B (string feature, int label);

	/** Creates the object from a provided array strings of a known length and a label
	 \param nbStr an array of strings, each corresponding one neighborhood in the IF part.
	 each string in the array is a string of 0s and 1s, not separated by anything. 
	 \param strNum The number of strings, equal to number of neighborhoods.
	 This function select the chunk of the string for each Neighborhood2 based on the size of NRADIUS.*/
	LongInstance2B (string* nbStr, int strNum, int label);
	
	/** Creates the object from a provided int array whose pointer is given, and a label. */
	//LongInstance2B (int* feature, int featureSize, int label);

	/** Creates the object around the given coordinate from a provided string and a label*/
	//LongInstance2B (string feature, int label, int row, int col);
	
	/** Creates the object around the given coordinate from a provided 
	 * int array whose pointer is given, and a label. 
	 */
	//LongInstance2B (int* feature, int featureSize, int label, int row, int col);
	
	//LongInstance2B (unsigned int totalHash, int size, int label);

	//LongInstance2B (unsigned int totalHash, int size, int label, int occ, int conf);

	//LongInstance2B (const EightLayerIM& images, int row, int col, int size); //Warning: LongInstance2B has no idea about EightLayerIM

	~LongInstance2B();

	bool operator== (const LongInstance2B& other) const;

	bool conflicts (const LongInstance2B& other) const;

	/** Returns the number of Neighborhood2 objects in the neighborhood2 vector. */
	int getNBNum() const;

	friend ostream& operator<< (ostream& stream, const LongInstance2B& li2b);
	friend void ConvertLI2BtoLI2 (const LongInstance2B& li2b, LongInstance2& li2);	
	friend bool li2bLabelIsZero (const LongInstance2B& li2b);
};

